package com.emmmya.harinCheck.engine.impl;

import com.emmmya.harinCheck.annotation.Check;
import com.emmmya.harinCheck.annotation.DeepCheck;
import com.emmmya.harinCheck.engine.AbsHarinCheckEngine;
import com.emmmya.harinCheck.exception.HarinCheckException;
import com.emmmya.harinCheck.result.CheckResult;
import com.emmmya.harinCheck.utils.BigDecimalUtils;
import com.emmmya.harinCheck.utils.HarinStringUtils;
import com.emmmya.harinCheck.utils.RegexUtils;

import java.lang.reflect.Field;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 针对@Check开发的默认校验器
 * 如使用的是字节码对象来调用check方法，必须使用public来修饰空参的构造函数
 * @author Harin
 * @version 1.0
 * @date 2022-04-29 19:38:54
 */
public class DefaultHarinCheckEngine extends AbsHarinCheckEngine {

    public DefaultHarinCheckEngine(){
        super();
    }

    /**
     * 深度校验
     * @param obj               待校验对象
     * @param fliedNames        字段名称
     * @param deep              是否深度校验
     * @return
     * @throws HarinCheckException
     * @throws IllegalAccessException
     */
    public CheckResult check(Object obj,String[] fliedNames,Boolean deep) throws HarinCheckException, IllegalAccessException {
        if(HarinStringUtils.isEmpty(obj)) throw new HarinCheckException("传入参数为空，操作终止！");
        Field[] allFields = getAllFields(obj);
        List<String> fliedNameList = fliedNames == null ? new ArrayList<>() : Arrays.asList(fliedNames);
        if(allFields != null){
            List<Field> fieldList = Arrays.stream(allFields).filter(e -> e.getAnnotation(Check.class) != null)
                    .collect(Collectors.toList());

            Map<String, List<Field>> nameAndfields =
                    fieldList.stream().collect(Collectors.groupingBy(e -> e.getAnnotation(Check.class).name()));

            if(fieldList == null && fieldList.isEmpty()) return CheckResult.adopt("校验成功，不存在带校验属性。");

            for (Field field : fieldList) {
                field.setAccessible(true);
                Check annotation = field.getAnnotation(Check.class);
                DeepCheck deepCheck = field.getAnnotation(DeepCheck.class);

                Object o = field.get(obj);

                if(deepCheck != null){
                    if ( o instanceof List ){
                       List<Object> dou =  (List<Object>) o;
                       if(dou != null && dou.size() > 0){
                           int index =0;
                           for (Object o1 : dou) {
                               if(o1 == null){
                                   return CheckResult.fail("数组中存在null值，校验失败", annotation.name(), o1);
                               }
                               CheckResult check = check(o1, null, deep);
                               index++;
                               if (check.getResult()) {
                                   continue;
                               }
                               LinkedList<String> deepMessage = check.getDeepMessage();
                               if(deepMessage == null){deepMessage= new LinkedList<>();}
                               deepMessage.addFirst(annotation.name()+"的第"+(index)+"条数据，存在错误信息");
                               check.setDeepMessage(deepMessage);
                               return check;
                           }
                       }
                    }
                    else {
                        if(o == null){
                            return CheckResult.fail("存在null值，深度校验失败", annotation.name(), o);
                        }
                        CheckResult check = check(o, null, deep);
                        if (check.getResult()) {
                            continue;
                        }
                        LinkedList<String> deepMessage = check.getDeepMessage();
                        if(deepMessage == null){deepMessage= new LinkedList<>();}
                        deepMessage.addFirst(annotation.name()+"存在错误信息");
                        check.setDeepMessage(deepMessage);
                        return check;
                    }
                }

                String name = annotation.name();
                //需要
                if (fliedNameList.isEmpty() || fliedNameList.contains(name)) {

                    boolean required = annotation.required();
                    int minL = annotation.minLength();
                    int maxL = annotation.maxLength();
                    String maxV = annotation.maxValue();
                    String minV = annotation.minValue();
                    String biggerName = annotation.bigger();
                    String smallName = annotation.small();

                    String defaultValue = annotation.defaultValue();
                    String pattern = annotation.pattern();
                    String errorMsg = annotation.errorMsg();

                    //赋予默认值
                    if (o == null && !HarinStringUtils.isEmpty(defaultValue)) {
                        //生成默认值
                        Object dv = generateDefaultValue(field, defaultValue);
                        field.set(obj, dv);
                    }

                    //必填非空
                    if (required) {
                        if (HarinStringUtils.isEmpty(o)) {
                            return CheckResult.fail(HarinStringUtils.isEmpty(errorMsg) ? name + "不能为空" : errorMsg, name, null);
                        }
                    }

                    //最小长度
                    if (minL != -1) {
                        if (String.class.isInstance(o) && String.valueOf(o).length() < minL) {
                            return CheckResult.fail(HarinStringUtils.isEmpty(errorMsg) ? name + "长度小于" + minL + "位" : errorMsg, name, o);
                        }
                    }
                    //最大长度
                    if (maxL != -1) {
                        if (String.class.isInstance(o) && String.valueOf(o).length() > maxL) {
                            return CheckResult.fail(HarinStringUtils.isEmpty(errorMsg) ? name + "长度大于" + maxL + "位" : errorMsg, name, o);
                        }
                    }
                    //最大值
                    if (!HarinStringUtils.isEmpty(maxV) && RegexUtils.findAny(NUMBER_PATTERN, o.toString())) {
                        if (RegexUtils.findAny(NUMBER_PATTERN, maxV)) {
                            //比较大小
                            int flag = BigDecimalUtils.compareTo(o.toString(), maxV);
                            if (flag > 0) {
                                return CheckResult.fail(HarinStringUtils.isEmpty(errorMsg) ? name + "大于最大值" + maxV : errorMsg, name, o);
                            }

                        }
                    }
                    //最小值
                    if (!HarinStringUtils.isEmpty(minV) && RegexUtils.findAny(NUMBER_PATTERN, o.toString())) {
                        if (RegexUtils.findAny(NUMBER_PATTERN, minV)) {
                            //比较大小
                            int flag = BigDecimalUtils.compareTo(minV, o.toString());
                            if (flag > 0) {
                                return CheckResult.fail(HarinStringUtils.isEmpty(errorMsg) ? name + "小于最小值" + minV : errorMsg, name, o);
                            }

                        }
                    }

                    //比XX值大，比较数值，时间格式
                    if(HarinStringUtils.isNotEmpty(biggerName)){
                        if(!biggerName.equals(name)){
                            List<Field> fields = nameAndfields.get(biggerName);
                            if(fields!= null && fields.size() == 1){
                                Field biggerField = fields.get(0);
                                biggerField.setAccessible(true);
                                //判断是否为相同类型
                                if( biggerField.get(obj) instanceof Number && field.get(obj) instanceof Number){
                                    Object o2 = biggerField.get(obj);
                                    int i = ((Double) ((Number) o).doubleValue()).compareTo(((Number) o2).doubleValue());
                                    if(i <= 0 ){
                                        return CheckResult.fail(HarinStringUtils.isEmpty(errorMsg) ?
                                                "必须"+name + "比" + biggerName + "大，"+name+"："+o+"，"+biggerName+":"+o2
                                                : errorMsg, name, o);
                                    }

                                }else if(biggerField.get(obj) instanceof Date && field.get(obj) instanceof Date){
                                    Object o2 = biggerField.get(obj);
                                    int i = ((Date) o).compareTo((Date) o2);
                                    if(i <= 0 ){
                                        return CheckResult.fail(HarinStringUtils.isEmpty(errorMsg) ?
                                                "必须"+name + "比" + biggerName + "大，"+name+"："+o+"，"+biggerName+":"+o2
                                                : errorMsg, name, o);
                                    }
                                }
                            }
                        }

                    }

                    //比XX值小，比较数值，时间格式
                    if(HarinStringUtils.isNotEmpty(smallName)){
                        if(!smallName.equals(name)) {
                            List<Field> fields = nameAndfields.get(smallName);
                            if (fields != null && fields.size() == 1) {
                                Field smallField = fields.get(0);
                                smallField.setAccessible(true);
                                //判断是否为相同类型
                                if (smallField.get(obj) instanceof Number && field.get(obj) instanceof Number) {
                                    Object o2 = smallField.get(obj);
                                    int i = ((Double) ((Number) o).doubleValue()).compareTo(((Number) o2).doubleValue());
                                    if (i >= 0) {
                                        return CheckResult.fail(HarinStringUtils.isEmpty(errorMsg) ?
                                                "必须" + name + "比" + smallField + "小，" + name + "：" + o + "，" + smallField + ":" + o2
                                                : errorMsg, name, o);
                                    }

                                } else if (smallField.get(obj) instanceof Date && field.get(obj) instanceof Date) {
                                    Object o2 = smallField.get(obj);
                                    int i = ((Date) o).compareTo((Date) o2);
                                    if (i >= 0) {
                                        return CheckResult.fail(HarinStringUtils.isEmpty(errorMsg) ?
                                                "必须" + name + "比" + smallField + "小，" + name + "：" + o + "，" + smallField + ":" + o2
                                                : errorMsg, name, o);
                                    }
                                }
                            }
                        }
                    }


                    if (HarinStringUtils.isNotEmpty(pattern) && HarinStringUtils.isNotEmpty(o) && o instanceof String) {
                        if (!RegexUtils.matches(pattern, o.toString())) {
                            return CheckResult.fail(HarinStringUtils.isEmpty(errorMsg) ? name + "不符合正则规则：" + pattern : errorMsg, name, o);
                        }
                    }

                }
            }

        }else{
            return CheckResult.fail("未获取到对应的对象的属性，操作终止！");
        }

        return CheckResult.adopt("校验成功");
    }


    /**
     * 普通校验
     * @param obj           待校验的数据实体对象
     * @param fliedNames    校验的字段，与@Check的name属性相同的校验。传入空时，则全量校验
     * @return              校验结果
     * @throws HarinCheckException
     */
    @Override
    public CheckResult check(Object obj,String[] fliedNames) throws HarinCheckException, IllegalAccessException {
        if(HarinStringUtils.isEmpty(obj)) throw new HarinCheckException("传入参数为空，操作终止！");
        Field[] allFields = getAllFields(obj);
        List<String> fliedNameList = fliedNames == null ? new ArrayList<>() : Arrays.asList(fliedNames);
        if(allFields != null){
            List<Field> fieldList = Arrays.stream(allFields).filter(e -> e.getAnnotation(Check.class) != null)
                    .collect(Collectors.toList());

            Map<String, List<Field>> nameAndfields =
                    fieldList.stream().collect(Collectors.groupingBy(e -> e.getAnnotation(Check.class).name()));

            if(fieldList == null && fieldList.isEmpty()) return CheckResult.adopt("校验成功，不存在带校验属性。");

            for (Field field : fieldList) {
                field.setAccessible(true);
                Check annotation = field.getAnnotation(Check.class);
                Object o = field.get(obj);
                String name = annotation.name();
                //需要
                if (fliedNameList.isEmpty() || fliedNameList.contains(name)) {

                    boolean required = annotation.required();
                    int minL = annotation.minLength();
                    int maxL = annotation.maxLength();
                    String maxV = annotation.maxValue();
                    String minV = annotation.minValue();
                    String biggerName = annotation.bigger();
                    String smallName = annotation.small();

                    String defaultValue = annotation.defaultValue();
                    String pattern = annotation.pattern();
                    String errorMsg = annotation.errorMsg();

                    //赋予默认值
                    if (o == null && !HarinStringUtils.isEmpty(defaultValue)) {
                        //生成默认值
                        Object dv = generateDefaultValue(field, defaultValue);
                        field.set(obj, dv);
                    }

                    //必填非空
                    if (required) {
                        if (HarinStringUtils.isEmpty(o)) {
                            return CheckResult.fail(HarinStringUtils.isEmpty(errorMsg) ? name + "不能为空" : errorMsg, name, null);
                        }
                    }

                    //最小长度
                    if (minL != -1) {
                        if (String.class.isInstance(o) && String.valueOf(o).length() < minL) {
                            return CheckResult.fail(HarinStringUtils.isEmpty(errorMsg) ? name + "长度小于" + minL + "位" : errorMsg, name, o);
                        }
                    }
                    //最大长度
                    if (maxL != -1) {
                        if (String.class.isInstance(o) && String.valueOf(o).length() > maxL) {
                            return CheckResult.fail(HarinStringUtils.isEmpty(errorMsg) ? name + "长度大于" + maxL + "位" : errorMsg, name, o);
                        }
                    }
                    //最大值
                    if (!HarinStringUtils.isEmpty(maxV) && RegexUtils.findAny(NUMBER_PATTERN, o.toString())) {
                        if (RegexUtils.findAny(NUMBER_PATTERN, maxV)) {
                            //比较大小
                            int flag = BigDecimalUtils.compareTo(o.toString(), maxV);
                            if (flag > 0) {
                                return CheckResult.fail(HarinStringUtils.isEmpty(errorMsg) ? name + "大于最大值" + maxV : errorMsg, name, o);
                            }

                        }
                    }
                    //最小值
                    if (!HarinStringUtils.isEmpty(minV) && RegexUtils.findAny(NUMBER_PATTERN, o.toString())) {
                        if (RegexUtils.findAny(NUMBER_PATTERN, minV)) {
                            //比较大小
                            int flag = BigDecimalUtils.compareTo(minV, o.toString());
                            if (flag > 0) {
                                return CheckResult.fail(HarinStringUtils.isEmpty(errorMsg) ? name + "小于最小值" + minV : errorMsg, name, o);
                            }

                        }
                    }

                    //比XX值大，比较数值，时间格式
                    if(HarinStringUtils.isNotEmpty(biggerName)){
                        if(!biggerName.equals(name)){
                            List<Field> fields = nameAndfields.get(biggerName);
                            if(fields!= null && fields.size() == 1){
                                Field biggerField = fields.get(0);
                                biggerField.setAccessible(true);
                                //判断是否为相同类型
                                if( biggerField.get(obj) instanceof Number && field.get(obj) instanceof Number){
                                    Object o2 = biggerField.get(obj);
                                    int i = ((Double) ((Number) o).doubleValue()).compareTo(((Number) o2).doubleValue());
                                    if(i <= 0 ){
                                        return CheckResult.fail(HarinStringUtils.isEmpty(errorMsg) ?
                                                "必须"+name + "比" + biggerName + "大，"+name+"："+o+"，"+biggerName+":"+o2
                                                : errorMsg, name, o);
                                    }

                                }else if(biggerField.get(obj) instanceof Date && field.get(obj) instanceof Date){
                                    Object o2 = biggerField.get(obj);
                                    int i = ((Date) o).compareTo((Date) o2);
                                    if(i <= 0 ){
                                        return CheckResult.fail(HarinStringUtils.isEmpty(errorMsg) ?
                                                "必须"+name + "比" + biggerName + "大，"+name+"："+o+"，"+biggerName+":"+o2
                                                : errorMsg, name, o);
                                    }
                                }
                            }
                        }

                    }

                    //比XX值小，比较数值，时间格式
                    if(HarinStringUtils.isNotEmpty(smallName)){
                        if(!smallName.equals(name)) {
                            List<Field> fields = nameAndfields.get(smallName);
                            if (fields != null && fields.size() == 1) {
                                Field smallField = fields.get(0);
                                smallField.setAccessible(true);
                                //判断是否为相同类型
                                if (smallField.get(obj) instanceof Number && field.get(obj) instanceof Number) {
                                    Object o2 = smallField.get(obj);
                                    int i = ((Double) ((Number) o).doubleValue()).compareTo(((Number) o2).doubleValue());
                                    if (i >= 0) {
                                        return CheckResult.fail(HarinStringUtils.isEmpty(errorMsg) ?
                                                "必须" + name + "比" + smallField + "小，" + name + "：" + o + "，" + smallField + ":" + o2
                                                : errorMsg, name, o);
                                    }

                                } else if (smallField.get(obj) instanceof Date && field.get(obj) instanceof Date) {
                                    Object o2 = smallField.get(obj);
                                    int i = ((Date) o).compareTo((Date) o2);
                                    if (i >= 0) {
                                        return CheckResult.fail(HarinStringUtils.isEmpty(errorMsg) ?
                                                "必须" + name + "比" + smallField + "小，" + name + "：" + o + "，" + smallField + ":" + o2
                                                : errorMsg, name, o);
                                    }
                                }
                            }
                        }
                    }


                    if (HarinStringUtils.isNotEmpty(pattern) && HarinStringUtils.isNotEmpty(o) && o instanceof String) {
                        if (!RegexUtils.matches(pattern, o.toString())) {
                            return CheckResult.fail(HarinStringUtils.isEmpty(errorMsg) ? name + "不符合正则规则：" + pattern : errorMsg, name, o);
                        }
                    }

                }
            }

        }else{
            return CheckResult.fail("未获取到对应的对象的属性，操作终止！");
        }

        return CheckResult.adopt("校验成功");
    }

    /**
     * 深度校验
     * @return
     * @throws HarinCheckException
     */
    @Override
    public CheckResult depthCheck(Object obj) throws HarinCheckException {
        return CheckResult.adopt("校验成功");
    }
}
